package cn.tedu.mall.product.service;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.pojo.product.dto.CategoryAddNewDTO;
import cn.tedu.mall.pojo.product.dto.CategoryUpdateBaseInfoDTO;
import cn.tedu.mall.pojo.product.dto.CategoryUpdateFullInfoDTO;
import cn.tedu.mall.pojo.product.model.Category;
import cn.tedu.mall.pojo.product.vo.CategoryStandardVO;
import cn.tedu.mall.product.constant.CommonConst;
import cn.tedu.mall.product.mapper.BrandCategoryMapper;
import cn.tedu.mall.product.mapper.CategoryAttributeTemplateMapper;
import cn.tedu.mall.product.mapper.CategoryMapper;
import cn.tedu.mall.product.redis.constant.RedisConst;
import cn.tedu.mall.product.redis.repo.ICategoryRedisRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.List;

import static cn.tedu.mall.product.redis.constant.RedisConst.REBUILD_INTERVAL_IN_MINUTE;

/**
 * <p>类别业务实现类</p>
 *
 * @author tedu.cn
 * @since 2021-11-30
 */
@Service
@Slf4j
public class CategoryServiceImpl implements ICategoryService {

    @Autowired
    private CategoryMapper categoryMapper;
    @Autowired
    private BrandCategoryMapper brandCategoryMapper;
    @Autowired
    private CategoryAttributeTemplateMapper categoryAttributeTemplateMapper;
    @Autowired
    private ICategoryRedisRepository categoryRedisRepository;

    @Override
    public Long addNew(CategoryAddNewDTO categoryAddNewDTO) {
        // 检查名称是否被占用
        Object checkNameQueryResult = categoryMapper.getByName(categoryAddNewDTO.getName());
        if (checkNameQueryResult != null) {
            throw new CoolSharkServiceException(ResponseCode.CONFLICT, "新增类别失败，类别名称(" + categoryAddNewDTO.getName() + ")已存在！");
        }

        // 如果未确定父级类别，则视为一级类别
        if (categoryAddNewDTO.getParentId() == null) {
            categoryAddNewDTO.setParentId(0L);
        }

        // 检查父级是否存在
        CategoryStandardVO parentCategory = categoryMapper.getById(categoryAddNewDTO.getParentId());
        if (categoryAddNewDTO.getParentId() != 0 && parentCategory == null) {
            throw new CoolSharkServiceException(ResponseCode.NOT_FOUND, "新增类别失败，父级类别不存在！");
        }

        // 复制属性到即将插入数据的对象中
        // 由于存在处理业务的可能，不推荐使用BeanUtils.copyProperties()方法
        Category category = new Category();
        category.setParentId(categoryAddNewDTO.getParentId());
        category.setName(categoryAddNewDTO.getName());
        category.setKeywords(categoryAddNewDTO.getKeywords());
        category.setIcon(categoryAddNewDTO.getIcon());
        category.setEnable(categoryAddNewDTO.getEnable());
        category.setDisplay(categoryAddNewDTO.getDisplay());
        category.setSort(categoryAddNewDTO.getSort() == null ? CommonConst.SORT_DEFAULT : categoryAddNewDTO.getSort());

        // 补全depth属性，即类别的深度
        int depth = 1;
        if (categoryAddNewDTO.getParentId() != 0) {
            depth = parentCategory.getDepth() + 1;
        }
        category.setDepth(depth);

        // 补全属性
        category.setParent(0); // 新增的类别默认不是父级

        // 执行插入数据
        int rows = categoryMapper.insert(category);
        if (rows != 1) {
            throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR, "新增类别失败，服务器忙，请稍后再次尝试！");
        }

        // 新增类别后，如果当前新增的类别不是1级类别，且父级类别的parent为0，则需更新为1
        if (categoryAddNewDTO.getParentId() != 0 && parentCategory.getParent() == 0) {
            rows = categoryMapper.updateParentById(parentCategory.getId(), 1);
            if (rows != 1) {
                throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR, "新增类别失败，服务器忙，请稍后再次尝试！");
            }
        }

        // 返回当前类别的id
        return category.getId();
    }

    @Override
    public void deleteById(Long id) {
        // 检查尝试删除的类别是否存在
        CategoryStandardVO currentCategory = categoryMapper.getById(id);
        if (currentCategory == null) {
            throw new CoolSharkServiceException(ResponseCode.NOT_FOUND, "删除类别失败，尝试访问的数据不存在！");
        }

        // 检查尝试删除的类别是否存在子级类别
        {
            int count = categoryMapper.countByParentId(id);
            if (count > 0) {
                throw new CoolSharkServiceException(ResponseCode.CONFLICT, "删除类别失败，当前类别下仍有子级类别！");
            }
        }

        // 检查尝试删除的类别是否与品牌绑定
        {
            int count = brandCategoryMapper.countByCategoryId(id);
            if (count > 0) {
                throw new CoolSharkServiceException(ResponseCode.CONFLICT, "删除类别失败，当前类别存在与品牌的绑定！");
            }
        }

        // 检查尝试删除的类别是否关联了属性模板
        {
            int count = categoryAttributeTemplateMapper.countByCategoryId(id);
            if (count > 0) {
                throw new CoolSharkServiceException(ResponseCode.CONFLICT, "删除类别失败，当前类别仍关联了属性模板！");
            }
        }

        // 执行删除
        int rows = categoryMapper.deleteById(id);
        if (rows != 1) {
            throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR, "删除类别失败，服务器忙，请稍后再次尝试！");
        }

        // 如果父级类别已经没有其它子级类别，则父级类型的is_parent更新为0
        if (currentCategory.getParentId() != 0) {
            CategoryStandardVO parentCategory = categoryMapper.getById(currentCategory.getParentId());
            if (parentCategory != null) {
                List<?> currentCategorySiblings = categoryMapper.listByParentId(parentCategory.getId());
                if (currentCategorySiblings.size() == 0) {
                    rows = categoryMapper.updateParentById(parentCategory.getId(), 0);
                    if (rows != 1) {
                        throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR, "删除类别失败，服务器忙，请稍后再次尝试！");
                    }
                }
            }
        }
    }

    @Override
    public void setEnableById(Long id) {
        // 检查尝试编辑的类别是否存在
        CategoryStandardVO currentCategory = categoryMapper.getById(id);
        if (currentCategory == null) {
            throw new CoolSharkServiceException(ResponseCode.NOT_FOUND, "启用类别失败，尝试访问的数据不存在！");
        }

        // 检查当前状态值
        if (currentCategory.getEnable().equals(1)) {
            throw new CoolSharkServiceException(ResponseCode.CONFLICT, "启用类别失败，类别当前已经启用！");
        }

        // 执行更新
        int rows = categoryMapper.updateEnableById(id, 1);
        if (rows != 1) {
            throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR, "启用类别失败，服务器忙，请稍后再次尝试！");
        }
    }

    @Override
    public void setDisableById(Long id) {
        // 检查尝试编辑的类别是否存在
        CategoryStandardVO currentCategory = categoryMapper.getById(id);
        if (currentCategory == null) {
            throw new CoolSharkServiceException(ResponseCode.NOT_FOUND, "禁用类别失败，尝试访问的数据不存在！");
        }

        // 检查当前状态值
        if (currentCategory.getEnable().equals(0)) {
            throw new CoolSharkServiceException(ResponseCode.CONFLICT, "禁用类别失败，类别当前已经启用！");
        }

        // 执行更新
        int rows = categoryMapper.updateEnableById(id, 0);
        if (rows != 1) {
            throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR, "禁用类别失败，服务器忙，请稍后再次尝试！");
        }
    }

    @Override
    public void setDisplayById(Long id) {
        // 检查尝试编辑的类别是否存在
        CategoryStandardVO currentCategory = categoryMapper.getById(id);
        if (currentCategory == null) {
            throw new CoolSharkServiceException(ResponseCode.NOT_FOUND, "显示类别失败，尝试访问的数据不存在！");
        }

        // 检查当前状态值
        if (currentCategory.getDisplay().equals(1)) {
            throw new CoolSharkServiceException(ResponseCode.CONFLICT, "显示类别失败，类别当前已经启用！");
        }

        // 执行更新
        int rows = categoryMapper.updateDisplayById(id, 1);
        if (rows != 1) {
            throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR, "显示类别失败，服务器忙，请稍后再次尝试！");
        }
    }

    @Override
    public void setHiddenById(Long id) {
        // 检查尝试编辑的类别是否存在
        CategoryStandardVO currentCategory = categoryMapper.getById(id);
        if (currentCategory == null) {
            throw new CoolSharkServiceException(ResponseCode.NOT_FOUND, "隐藏类别失败，尝试访问的数据不存在！");
        }

        // 检查当前状态值
        if (currentCategory.getDisplay().equals(0)) {
            throw new CoolSharkServiceException(ResponseCode.CONFLICT, "隐藏类别失败，类别当前已经启用！");
        }

        // 执行更新
        int rows = categoryMapper.updateDisplayById(id, 0);
        if (rows != 1) {
            throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR, "隐藏类别失败，服务器忙，请稍后再次尝试！");
        }
    }

    @Override
    public void updateBaseInfoById(Long id, CategoryUpdateBaseInfoDTO categoryUpdateBaseInfoDTO) {
        // 检查尝试编辑的类别是否存在
        CategoryStandardVO currentCategory = categoryMapper.getById(id);
        if (currentCategory == null) {
            throw new CoolSharkServiceException(ResponseCode.NOT_FOUND, "更新类别基本信息失败，尝试访问的数据不存在！");
        }

        // 检查新名称是否冲突
        CategoryStandardVO checkNameQueryResult = categoryMapper.getByName(categoryUpdateBaseInfoDTO.getName());
        if (checkNameQueryResult != null && !checkNameQueryResult.getId().equals(id)) {
            throw new CoolSharkServiceException(ResponseCode.CONFLICT, "更新类别基本信息失败，类别名称(" + categoryUpdateBaseInfoDTO.getName() + ")已存在！");
        }

        // 执行更新
        Category category = new Category();
        BeanUtils.copyProperties(categoryUpdateBaseInfoDTO, category);
        category.setId(id);
        int rows = categoryMapper.updateBaseInfoById(category);
        if (rows != 1) {
            throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR, "更新类别基本信息失败，服务器忙，请稍后再次尝试！");
        }
    }

    @Override
    public void updateFullInfoById(Long id, CategoryUpdateFullInfoDTO categoryUpdateFullInfoDTO) {
        // 检查尝试编辑的类别是否存在
        CategoryStandardVO currentCategory = categoryMapper.getById(id);
        if (currentCategory == null) {
            throw new CoolSharkServiceException(ResponseCode.NOT_FOUND, "更新类别信息失败，尝试访问的数据不存在！");
        }

        // 检查新名称是否冲突
        CategoryStandardVO checkNameQueryResult = categoryMapper.getByName(categoryUpdateFullInfoDTO.getName());
        if (checkNameQueryResult != null && !checkNameQueryResult.getId().equals(id)) {
            throw new CoolSharkServiceException(ResponseCode.CONFLICT, "更新类别信息失败，类别名称(" + categoryUpdateFullInfoDTO.getName() + ")已存在！");
        }

        // 执行更新
        Category category = new Category();
        BeanUtils.copyProperties(categoryUpdateFullInfoDTO, category);
        category.setId(id);
        int rows = categoryMapper.updateBaseInfoById(category);
        if (rows != 1) {
            throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR, "更新类别信息失败，服务器忙，请稍后再次尝试！");
        }
    }

    @Override
    public CategoryStandardVO getById(Long id) {
        // 尝试从Redis中获取数据
        Serializable redisData = categoryRedisRepository.getValue(id);
        // 检查Redis中的数据是否存在
        if (redisData == null) {
            // Redis中没有与此id匹配的数据，尝试从数据库中获取数据
            CategoryStandardVO category = categoryMapper.getById(id);
            // 判断从数据库中是否获取到数据
            if (category == null) {
                // 数据库中仍无此数据，为避免缓存穿透，在Redis中增加空数据
                categoryRedisRepository.setEmptyValue(id);
                // 返回错误
                throw new CoolSharkServiceException(ResponseCode.NOT_FOUND, "获取类别详情失败，尝试访问的数据不存在！");
            } else {
                // 数据库中有此数据，则写入缓存
                categoryRedisRepository.setValue(category);
                // 返回有效数据
                return category;
            }
        }
        // 检查Redis中的数据是否为空数据
        if (RedisConst.EMPTY_VALUE.equals(redisData.toString())) {
            // 返回错误
            throw new CoolSharkServiceException(ResponseCode.NOT_FOUND, "获取类别详情失败，尝试访问的数据不存在！");
        }
        // 返回有效值
        return (CategoryStandardVO) redisData;
    }

    @Override
    public List<CategoryStandardVO> listByBrand(Long brandId) {
        return categoryRedisRepository.listByBrand(brandId);
    }

    @Override
    public List<CategoryStandardVO> listByParent(Long parentId) {
        return categoryRedisRepository.listByParent(parentId);
    }

    @Override
    public void initCache() {
        loadCache();
    }

    @Override
    public void rebuildCache() {
        // 日志
        log.debug("准备重建类别数据缓存……");
        // 获取最近加载（全部）缓存时间
        Long recentlyLoadTime = categoryRedisRepository.getRecentlyLoadTime();
        // 判断记录的时间是否存在
        if (recentlyLoadTime != null) {
            // 判断间隔时间
            long currentTime = System.currentTimeMillis();
            log.debug("最近加载（全部）缓存时间为{}，当前时间为{}，时间差为{}", recentlyLoadTime, currentTime, (currentTime - recentlyLoadTime));
            // 判断是否允许重建品牌数据缓存
            if ((currentTime - recentlyLoadTime) < REBUILD_INTERVAL_IN_MINUTE * 60 * 1000) {
                throw new CoolSharkServiceException(ResponseCode.NOT_ACCEPTABLE,
                        "重建缓存失败，间隔时间必须大于" + REBUILD_INTERVAL_IN_MINUTE + "分钟！");
            }
        }
        // 执行重建品牌数据缓存
        loadCache();
    }

    /**
     * 加载品牌的全部缓存数据
     */
    private void loadCache() {
        // 日志
        log.debug("即将向Redis中写入类别数据缓存……");
        // 清空原有缓存（仅作用于当前类型数据）
        log.debug("清空Redis中原有类别数据缓存……");
        categoryRedisRepository.flushAll();
        log.debug("已经清空Redis中原有类别数据缓存");

        {
            // 加载数据库中的数据
            List<CategoryStandardVO> categories = categoryMapper.list();
            // 遍历查询结果
            log.debug("向Redis中写入各个类别数据……");
            for (CategoryStandardVO categoryStandardVO : categories) {
                // 逐一写入到Redis
                categoryRedisRepository.setValue(categoryStandardVO);
            }

            // 将整个列表写入到Redis
            log.debug("向Redis中写入类别列表……");
            categoryRedisRepository.appendList(categories);
        }

        // 获取与品牌有关联的类别列表
        List<Long> brandIdList = brandCategoryMapper.listBrandIds();
        // 遍历类别id，存入对应的品牌列表
        log.debug("向Redis中写入各品牌对应的类别列表……");
        for (Long brandId : brandIdList) {
            List<CategoryStandardVO> categories = categoryMapper.listByBrandId(brandId);
            categoryRedisRepository.setListForBrand(brandId, categories);
        }

        // 获取所有父级类别的id的列表
        List<Long> parentIdList = categoryMapper.listParentIds();
        parentIdList.add(0, 0L);
        // 遍历类别id，存入对应的品牌列表
        log.debug("向Redis中写入各品牌对应的类别列表……");
        for (Long parentId : parentIdList) {
            List<CategoryStandardVO> categories = categoryMapper.listByParentId(parentId);
            categoryRedisRepository.setListForParent(parentId, categories);
        }

        // 记录此次加载缓存的时间
        categoryRedisRepository.setRecentlyLoadTime();

        // 日志
        log.debug("向Redis中写入类别数据缓存，完成！");
    }

}
